/**
* \file SCase.c
* \author Catalin BLAJ
* \author Vincent JACQUEMIN
* \date 17 Octobre 2013
*
* Fichier source SCase.c pour le programme du Sudoku
*/

#include "SCase.h"


SCase* SC_initialiser(unsigned char type, unsigned indice)
{
    /* Variables de convenance */
    unsigned nombreCases = type*type*type*type;

    /* Vérification des paramètres */
    assert((type>=2) && (type<=4));
    assert(indice<nombreCases);

    /* Allocation mémoire et affectation des champs de structure */
    SCase* maCase = malloc(sizeof(SCase));
    maCase->type = type;
    maCase->indice = indice;
    SC_setEtat(maCase, vide);
    SC_setCandidats(maCase, 0);
    SC_setValeur(maCase, -1);

    return maCase;
}


void SC_testament(SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On libère l'espace alloué */
    free(maCase);
}


unsigned char SC_getType(const SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On renvoit le type du Sudoku */
    return maCase->type;
}


short SC_getValeur(const SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On renvoit la valeur de la structure SCase */
    return maCase->valeur;
}


unsigned SC_getIndice(const SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On renvoit l'indice de la structure SCase */
    return maCase->indice;
}


Etat SC_getEtat(const SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On renvoit l'etat de la structure SCase */
    return maCase->etat;
}


bool SC_getCandidat(const SCase* maCase, unsigned short candidat)
{
    /* Variables de convenance */
    unsigned char type = SC_getType(maCase);
    unsigned short nombreCandidats = type*type;

    /* Vérification des paramètres */
    assert(maCase != NULL);
    assert(candidat<nombreCandidats);

    /* On fait une opération & binaire entre le candidat et les candidats de la case, si le résultat est différent de 0, le candidat s'y trouve */
    return (pow2(candidat) & SC_getTousCandidats(maCase));
}


unsigned int SC_getTousCandidats(const SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On renvoit le nombre correspondant aux candidats de ma structure SCase */
    return maCase->candidats;
}


unsigned short SC_getNombreCandidats(const SCase* maCase)
{
    /* Variables de convenance */
    unsigned char type = SC_getType(maCase);
    unsigned short nombreTotalCandidats = type*type;

    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* Variables */
    char nombreCandidats = 0;
    unsigned candidatsCase = SC_getTousCandidats(maCase);

    /*  */
    for (int i=0; i<nombreTotalCandidats; i++)
    {
        /* Si le candidat s'y trouve, on incrémente le compteur */
        if ((candidatsCase&1) == 1)
            nombreCandidats++;
        candidatsCase >>= 1;
    }

    /* On renvoit le nombre de candidats de la case */
    return nombreCandidats;
}


void SC_setValeur(SCase* maCase, short valeur)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On affecte la valeur en paramètre à la valeur de la case */
    maCase->valeur = valeur;
}


void SC_setEtat(SCase* maCase, Etat etat)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    /* On affecte l'état en paramètre à l'état de la case */
    maCase->etat = etat;
}


void SC_retirerCandidat(SCase* maCase, unsigned short candidat)
{
    /* Variables de convenance */
    unsigned char type = SC_getType(maCase);
    unsigned nombreCandidats = type*type;

    /* Vérification des paramètres */
    assert(maCase != NULL);
    assert(candidat<nombreCandidats);

    /* On transforme le candidat en paramètre en binaire : Ex : 2 = 000000010 */
    unsigned candidatB = pow2(candidat);
    /* On inverse tous les 0 et les 1 (le 0 sera à l'emplacement du candidat) : Ex 2 = 111111101 */
    candidatB ^= pow2(nombreCandidats)-1;
    /* On effectue une opération AND ce qui a pour effet d'enlever le candidat à l'emplacement du 0 */
    maCase->candidats &= candidatB;
}


void SC_ajouterCandidat(SCase* maCase, unsigned short candidat)
{
    /* Variables de convenance */
    unsigned char type = SC_getType(maCase);
    unsigned nombreCandidats = type*type;

    /* Vérification des paramètres */
    assert(maCase != NULL);
    assert(candidat<nombreCandidats);

    /* On transforme le candidat en paramètre en binaire : Ex : 2 = 000000010 */
    unsigned candidatB = pow2(candidat);
    /* On effectue une opération OR ce qui a pour effet d'ajouter le candidat à l'emplacement du 1 */
    maCase->candidats |= candidatB;
}


void SC_reinitialiserCandidats(SCase* maCase)
{
    /* Variables de convenance */
    unsigned char type = SC_getType(maCase);
    unsigned nombreCandidats = type*type;

    /* Vérification des paramètres */
    assert(maCase != NULL);

    maCase->candidats = pow2(nombreCandidats)-1;
}


void SC_setCandidats(SCase* maCase, unsigned int candidats)
{
    /* Variables de convenance */
    unsigned char type = SC_getType(maCase);
    unsigned nombreCandidats = type*type;

    /* Vérification des paramètres */
    assert(maCase != NULL);
    assert(candidats<pow2(nombreCandidats));

    maCase->candidats = candidats;
}


char* SC_toChaine(const SCase* maCase)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);

    char separateur[2] = ";";

    /* On met l'état de la case dans la chaine */
    unsigned etatI = maCase->etat;
    char* saveString = malloc(10*sizeof(char));
    sprintf(saveString, "%u", etatI);

    /* Conversion de la valeur ou du candidat selon l'état en string */
    int valeurOuCandidatI = 0;
    if (maCase->etat == vide)
        valeurOuCandidatI = maCase->candidats;
    else
        valeurOuCandidatI = maCase->valeur;

    char valeurOuCandidatS[7];
    sprintf(valeurOuCandidatS, "%d", valeurOuCandidatI);

    /* Concatenation avec le séparateur et la valeur ou le candidat (selon le cas) */
    strcat(saveString, separateur);
    strcat(saveString, valeurOuCandidatS);

    return saveString;
}


void SC_chargement(SCase* maCase, const char* sauvegarde)
{
    /* Vérification des paramètres */
    assert(maCase != NULL);
    assert(sauvegarde != NULL);

    char* candidatsS = NULL;
    char* valeurS = NULL;
    int candidatsI = 0;
    int valeurI = 0;

    /* Les caractères après le point-virgule correspondent au flag du candidat si la case est vide */
    if (sauvegarde[0] == '0')
    {
        SC_setEtat(maCase, vide);
        candidatsS = extraireSousChaine(sauvegarde, 2, strlen(sauvegarde));
        candidatsI = atoi(candidatsS);
        SC_setCandidats(maCase, candidatsI);
        free(candidatsS);
    }
    /* Les caractères après le point-virgule correspondent à la valeur si la case est validée ou gelée */
    else if (sauvegarde[0] == '1')
    {
        SC_setEtat(maCase, valide);
        valeurS = extraireSousChaine(sauvegarde, 2, strlen(sauvegarde));
        valeurI = atoi(valeurS);
        SC_setCandidats(maCase, pow2(valeurI));
        SC_setValeur(maCase, valeurI);
        free(valeurS);
    }
    else if (sauvegarde[0] == '2')
    {
        SC_setEtat(maCase, gele);
        valeurS = extraireSousChaine(sauvegarde, 2, strlen(sauvegarde));
        valeurI = atoi(valeurS);
        SC_setCandidats(maCase, pow2(valeurI));
        SC_setValeur(maCase, valeurI);
        free(valeurS);
    }
}
